package com.opNaga.bpmp.flowObject.activity;

import com.opNaga.bpmp.BPMPElement;
import com.opNaga.bpmp.connectingObject.messageFlow.MessageFlow;
import com.opNaga.bpmp.connectingObject.messageFlow.MessageFlowSource;
import com.opNaga.bpmp.connectingObject.messageFlow.MessageFlowTarget;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlow;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowSource;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowTarget;
import com.opNaga.bpmp.flowObject.FlowObject;
import com.opNaga.bpmp.requirement.HasNetworkRequirement;
import com.opNaga.bpmp.requirement.HasRequirement;
import com.opNaga.bpmp.requirement.NetworkRequirement;
import com.opNaga.bpmp.user.User;

import java.util.*;

/**
 * User: Junyi BEI
 * Date: 19/01/2012
 * Time: 13:58
 */
public abstract class Activity extends FlowObject implements SequenceFlowSource, SequenceFlowTarget, MessageFlowSource, MessageFlowTarget, PortalEntry, HasNetworkRequirement {
  protected Set<BPMPElement> outwardSequenceFlows = new HashSet<BPMPElement>();
  protected Set<BPMPElement> inwardSequenceFlows = new HashSet<BPMPElement>();
  protected Set<BPMPElement> outwardMessageFlows = new HashSet<BPMPElement>();
  protected Set<BPMPElement> inwardMessageFlows = new HashSet<BPMPElement>();
  protected Set<BPMPElement> portals = new HashSet<BPMPElement>();
  protected Set<BPMPElement> networkRequirements = new HashSet<BPMPElement>();

  public Activity() {
    super();
  }

  public Activity(String t) {
    super(t);
  }

  @Override
  public boolean destroy() {
    Iterator<BPMPElement> osFit = iterateOutwardSequenceFlows();
    while(osFit.hasNext()) {
      BPMPElement be = osFit.next();
      if(!be.destroy()) {
        return standardMessage(800, be);
      }
    }
    Iterator<BPMPElement> isfIt = iterateInwardSequenceFlows();
    while(isfIt.hasNext()) {
      BPMPElement be = isfIt.next();
      if(!be.destroy()) {
        return standardMessage(801, be);
      }
    }
    Iterator<BPMPElement> omfIt = iterateOutwardMessageFlows();
    while(omfIt.hasNext()) {
      BPMPElement be = omfIt.next();
      if(!be.destroy()) {
        return standardMessage(800, be);
      }
    }
    Iterator<BPMPElement> imfIt = iterateInwardMessageFlows();
    while(imfIt.hasNext()) {
      BPMPElement be = imfIt.next();
      if(!be.destroy()) {
        return standardMessage(801, be);
      }
    }
    Iterator<BPMPElement> pIt = iteratePortals();
    while(pIt.hasNext()) {
      BPMPElement be = pIt.next();
      if(!be.destroy()) {
        return standardMessage(860, be);
      }
    }
    return super.destroy();
  }

  public Iterator<BPMPElement> iterateInwardSequenceFlows() {
    return new ArrayList<BPMPElement>(inwardSequenceFlows).iterator();
  }

  public boolean addOutwardMessageFlow(BPMPElement be) {
    if(!(be instanceof MessageFlow)) {
      return standardMessage(110, be);
    }
    if(!outwardMessageFlows.add(be)) {
      return standardMessage(111, be);
    }
    return standardMessage(191, be);
  }

  public boolean removeOutwardMessageFlow(BPMPElement be) {
    if(!(be instanceof MessageFlow)) {
      return standardMessage(210, be);
    }
    if(!outwardMessageFlows.remove(be)) {
      return standardMessage(211, be);
    }
    return standardMessage(291, be);
  }

  public boolean isOutwardMessageFlowEmpty() {
    return outwardMessageFlows.isEmpty();
  }

  public int outwardMessageFlowSize() {
    return outwardMessageFlows.size();
  }

  public Iterator<BPMPElement> iterateOutwardMessageFlows() {
    return new ArrayList<BPMPElement>(outwardMessageFlows).iterator();
  }

  public boolean addInwardMessageFlow(BPMPElement be) {
    if(!(be instanceof MessageFlow)) {
      return standardMessage(140, be);
    }
    if(!inwardMessageFlows.add(be)) {
      return standardMessage(141, be);
    }
    return standardMessage(194, be);
  }

  public boolean removeInwardMessageFlow(BPMPElement be) {
    if(!(be instanceof MessageFlow)) {
      return standardMessage(240, be);
    }
    if(!inwardMessageFlows.remove(be)) {
      return standardMessage(241, be);
    }
    return standardMessage(294, be);
  }

  public boolean isInwardMessageFlowEmpty() {
    return inwardMessageFlows.isEmpty();
  }

  public int inwardMessageFlowSize() {
    return inwardMessageFlows.size();
  }

  public Iterator<BPMPElement> iterateInwardMessageFlows() {
    return new ArrayList<BPMPElement>(inwardMessageFlows).iterator();
  }

  public boolean addOutwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(100, be);
    }
    if(!outwardSequenceFlows.add(be)) {
      return standardMessage(101, be);
    }
    return standardMessage(190, be);
  }

  public boolean removeOutwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(200, be);
    }
    if(!outwardSequenceFlows.remove(be)) {
      return standardMessage(201, be);
    }
    return standardMessage(290, be);
  }

  public boolean isOutwardSequenceFlowEmpty() {
    return outwardSequenceFlows.isEmpty();
  }

  public int outwardSequenceFlowSize() {
    return outwardSequenceFlows.size();
  }

  public Iterator<BPMPElement> iterateOutwardSequenceFlows() {
    return new ArrayList<BPMPElement>(outwardSequenceFlows).iterator();
  }

  public boolean addInwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(130, be);
    }
    if(!inwardSequenceFlows.add(be)) {
      return standardMessage(131, be);
    }
    return standardMessage(193, be);
  }

  public boolean removeInwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(230, be);
    }
    if(!inwardSequenceFlows.remove(be)) {
      return standardMessage(231, be);
    }
    return standardMessage(293, be);
  }

  public boolean isInwardSequenceFlowEmpty() {
    return inwardSequenceFlows.isEmpty();
  }

  public int inwardSequenceFlowSize() {
    return inwardSequenceFlows.size();
  }

  public boolean addPortal(BPMPElement be) {
    if(!Portal.class.isAssignableFrom(be.getClass())) {
      return standardMessage(600, be);
    }
    if(!((Portal) be).setEntry(this)) {
      return standardMessage(601, be);
    }
    if(!portals.add(be)) {
      return standardMessage(602, be);
    }
    return standardMessage(650, be);
  }

  public boolean removePortal(BPMPElement be) {
    if(!(be instanceof Portal)) {
      return standardMessage(610, be);
    }
    if(!((Portal) be).setEntry(null)) {
      return standardMessage(611, be);
    }
    if(!portals.remove(be)) {
      return standardMessage(612, be);
    }
    return standardMessage(641, be);
  }

  public Iterator<BPMPElement> iteratePortals() {
    return new ArrayList<BPMPElement>(portals).iterator();
  }

  public boolean addNetworkRequirement(BPMPElement bc) {
    if(!NetworkRequirement.class.isAssignableFrom(bc.getClass())) {
      return standardMessage(770, bc);
    }
    if(!networkRequirements.add(bc)) {
      return standardMessage(771, bc);
    }
    return standardMessage(778, bc);
  }

  public boolean removeNetworkRequirement(BPMPElement bc) {
    if(!NetworkRequirement.class.isAssignableFrom(bc.getClass())) {
      return standardMessage(772, bc);
    }
    if(!networkRequirements.remove(bc)) {
      return standardMessage(773, bc);
    }
    return standardMessage(779, bc);
  }

  public Iterator<BPMPElement> iterateNetworkRequirements() {
    return new ArrayList<BPMPElement>(networkRequirements).iterator();
  }

  public boolean filterAgainstNetworkRequirements(Collection<BPMPElement> bes) {
    int before = bes.size();
    for(BPMPElement be : new ArrayList<BPMPElement>(bes)) {
      Iterator<BPMPElement> nrIt = iterateNetworkRequirements();
      while(nrIt.hasNext()) {
        BPMPElement nr = nrIt.next();
        Iterator<BPMPElement> nwIt = ((User) be).iterateNetworks();
        boolean inNetwork = false;
        while(nwIt.hasNext()) {
          BPMPElement nw = nwIt.next();
          Collection<BPMPElement> nwc = ((NetworkRequirement) nw).getCandidates();
          if(nwc.contains(nr)) {
            inNetwork = true;
            standardMessage(993, be, nr);
            break;
          }
        }
        if(!inNetwork) {
          bes.remove(be);
          standardMessage(994, be, nr);
          break;
        }
      }
    }
    int after = bes.size();
    return standardMessage(991, after, before);
  }

  public boolean filterAgainstAllRequirements(Collection<BPMPElement> bes) {
    if(container != null) {
      if(HasRequirement.class.isAssignableFrom(container.getClass())) {
        if(!((HasRequirement) container).filterAgainstAllRequirements(bes)) {
          return standardMessage(982, container);
        }
      } else {
        return standardMessage(981, container);
      }
    } else {
      return standardMessage(980);
    }
    return filterAgainstNetworkRequirements(bes);
  }

  public BPMPElement linkTo(BPMPElement be) {
    if(SequenceFlowTarget.class.isAssignableFrom(be.getClass()) && ((SequenceFlowTarget) be).getRoot() == root) {
      BPMPElement sf;
      if(manager != null) {
        if(be.getManager() == manager) {
          sf = manager.createInstance("sf", "-s=" + name, "-t=" + be.getName());
          if(sf != null) {
            standardMessage(910, be, sf);
            return sf;
          } else {
            standardMessage(903, be);
            return null;
          }
        } else {
          standardMessage(902, be);
          return null;
        }
      } else {
        sf = new SequenceFlow();
        if(!((SequenceFlow) sf).setTarget(be)) {
          sf.destroy();
          standardMessage(900, be, sf);
          return null;
        }
        if(!((SequenceFlow) sf).setSource(this)) {
          sf.destroy();
          standardMessage(901, be, sf);
          return null;
        }
        if(standardMessage(911, be, sf)) {
          return sf;
        }
      }
    }
    if(MessageFlowTarget.class.isAssignableFrom(be.getClass()) && ((MessageFlowTarget) be).getRoot() != root) {
      BPMPElement mf;
      if(manager != null) {
        if(be.getManager() == manager) {
          mf = manager.createInstance("mf", "-s=" + name, "-t=" + be.getName());
          if(mf != null) {
            standardMessage(910, be, mf);
            return mf;
          } else {
            standardMessage(903, be);
            return null;
          }
        } else {
          standardMessage(902, be);
          return null;
        }
      } else {
        mf = new MessageFlow();
        if(!((MessageFlow) mf).setTarget(be)) {
          mf.destroy();
          standardMessage(900, be, mf);
          return null;
        }
        if(!((MessageFlow) mf).setSource(this)) {
          mf.destroy();
          standardMessage(901, be, mf);
          return null;
        }
        if(standardMessage(911, be, mf)) {
          return mf;
        }
      }
    }
    return super.linkTo(be);
  }

  public BPMPElement getLinkTo(BPMPElement be) {
    if(SequenceFlowTarget.class.isAssignableFrom(be.getClass()) && ((SequenceFlowTarget) be).getRoot() == root) {
      Iterator<BPMPElement> osfIt = iterateOutwardSequenceFlows();
      while(osfIt.hasNext()) {
        BPMPElement osf = osfIt.next();
        if(((SequenceFlow) osf).getTarget() == be) {
          standardMessage(950, be, osf);
          return osf;
        }
      }
      standardMessage(940, be);
      return null;
    }
    if(MessageFlowTarget.class.isAssignableFrom(be.getClass()) && ((MessageFlowTarget) be).getRoot() != root) {
      Iterator<BPMPElement> omfIt = iterateOutwardMessageFlows();
      while(omfIt.hasNext()) {
        BPMPElement omf = omfIt.next();
        if(((MessageFlow) omf).getTarget() == be) {
          standardMessage(950, be, omf);
          return omf;
        }
      }
      standardMessage(940, be);
      return null;
    }
    return super.getLinkTo(be);
  }

  public BPMPElement linkFrom(BPMPElement be) {
    if(SequenceFlowSource.class.isAssignableFrom(be.getClass()) && ((SequenceFlowSource) be).getRoot() == root) {
      BPMPElement sf;
      if(manager != null) {
        if(be.getManager() == manager) {
          sf = manager.createInstance("sf", "-s=" + be.getName(), "-t=" + name);
          if(sf != null) {
            standardMessage(930, be, sf);
            return sf;
          } else {
            standardMessage(923, be);
            return null;
          }
        } else {
          standardMessage(922, be);
          return null;
        }
      } else {
        sf = new SequenceFlow();
        if(!((SequenceFlow) sf).setTarget(this)) {
          sf.destroy();
          standardMessage(920, be, sf);
          return null;
        }
        if(!((SequenceFlow) sf).setSource(be)) {
          sf.destroy();
          standardMessage(921, be, sf);
          return null;
        }
        if(standardMessage(931, be, sf)) {
          return sf;
        }
      }
    }
    if(MessageFlowSource.class.isAssignableFrom(be.getClass()) && ((MessageFlowSource) be).getRoot() != root) {
      BPMPElement mf;
      if(manager != null) {
        if(be.getManager() == manager) {
          mf = manager.createInstance("mf", "-s=" + be.getName(), "-t=" + name);
          if(mf != null) {
            standardMessage(930, be, mf);
            return mf;
          } else {
            standardMessage(923, be);
            return null;
          }
        } else {
          standardMessage(922, be);
          return null;
        }
      } else {
        mf = new MessageFlow();
        if(!((MessageFlow) mf).setTarget(this)) {
          mf.destroy();
          standardMessage(920, be, mf);
          return null;
        }
        if(!((MessageFlow) mf).setSource(be)) {
          mf.destroy();
          standardMessage(921, be, mf);
          return null;
        }
        if(standardMessage(931, be, mf)) {
          return mf;
        }
      }
    }
    return super.linkFrom(this);
  }

  public BPMPElement getLinkFrom(BPMPElement be) {
    if(SequenceFlowSource.class.isAssignableFrom(be.getClass()) && ((SequenceFlowSource) be).getRoot() == root) {
      Iterator<BPMPElement> isfIt = iterateInwardSequenceFlows();
      while(isfIt.hasNext()) {
        BPMPElement isf = isfIt.next();
        if(((SequenceFlow) isf).getSource() == be) {
          if(standardMessage(970, be, isf)) {
            return isf;
          } else {
            return null;
          }
        }
      }
      standardMessage(960, be);
      return null;
    }
    if(MessageFlowSource.class.isAssignableFrom(be.getClass()) && ((MessageFlowSource) be).getRoot() != root) {
      Iterator<BPMPElement> imfIt = iterateInwardMessageFlows();
      while(imfIt.hasNext()) {
        BPMPElement imf = imfIt.next();
        if(((MessageFlow) imf).getSource() == be) {
          if(standardMessage(970, be, imf)) {
            return imf;
          } else {
            return null;
          }
        }
      }
      standardMessage(960, be);
      return null;
    }
    return super.getLinkFrom(be);
  }
}
